[!NOTE]
This is one of 199 standalone projects, maintained as part
of the @thi.ng/umbrella monorepo
and anti-framework.
🚀 Please help me to work full-time on these projects by sponsoring me on
GitHub. Thank you! ❤️
About
Optimized 2d/3d/4d and arbitrary length vector operations, support for memory mapping/layouts.
Likely the most comprehensive vector library for TypeScript / JavaScript
currently available.
This package provides over 875(!) largely code generated functions and
supporting types to perform vector operations on fixed and arbitrary-length
vectors, both packed and strided (i.e. where individual vector components are
not successive array elements, for example in SOA memory
layouts).
Includes componentwise logic operations for boolean vectors, componentwise
comparisons for numeric vectors and componentwise binary ops for signed &
unsigned integer vectors.
Features
- Small & fast: The vast majority of functions are code generated with
fixed-sized versions not using any loops. Minified + gzipped, the entire
package is ~11.8KB (though you'll hardly ever use all functions).
- Unified API: Any
ArrayLike
type can be used as vector containers (e.g. JS
arrays, typed arrays, custom impls). Most functions are implemented as
multi-methods, dispatching to any potentially optimized versions based on
given vector arguments. - Highly modular: Each function is defined in its own submodule / file. In
addition to each generic multi-method base function, all fixed-length
optimized versions are exported too. E.g. If
add
performs vector addition on arbitrary-length vectors, add2
, add3
, add4
are the optimized version for fixed-length vectors... - Pluggable interface: The
VecAPI
interface
defines objects of the ~70 most common vector operations implemented for
specific vector sizes. Using this interface simplifies performance-critical
use cases & algorithms which target different dimensions (e.g. 2d/3d),
but should use the avaiable size-optimized vector ops. See
VEC2
,
VEC3
and
VEC4
- Extensible: Custom vector ops can be defined in a similar manner using the
provided code generation helpers (see
vop.ts
and
emit.ts
for details).
- Immutable by default: Each operation producing a vector result takes an output
vector as first argument. If
null
, the vector given as 2nd argument will
(usually) be used as output (i.e. for mutation). - Strided vector support is handled via the lightweight
Vec2/3/4
class wrappers and the
gvec()
proxy (for generic, arbitrary-length vectors). These types behave like normal
arrays (for read/write operations) and are also iterable. A subset of
functions (suffixed with S
, e.g.
addS
vs. add
) also support striding without the need for extra class wrappers.
This is handled via additional index and stride arguments for each
input/output vector. These functions are only available for sizes 2 / 3 / 4,
though. - Random vector functions support the
IRandom
interface defined by
@thi.ng/random
to work with custom (P)RNGs. If omitted, the built-in Math.random()
will be
used.
Partially ported from thi.ng/geom-clj (Clojure) and
c.thi.ng (C11).
Status
STABLE - used in production
Search or submit any issues for this package
Breaking changes in v6.0.0
The introduction of seveveral standard libc math
functions to the
@thi.ng/math
package caused a behavior change of existing fmod()
function. For symmetry
reasons the same changes have been applied to this package...
- swap
fmod()
⇄ mod()
, to align the latter with its GLSL counterpart - the new
fmod()
has standard libc behavior (same as JS %
operator) - add
remainder()
with standard libc behavior
Breaking changes in v3.0.0
- to avoid confusion, the arg order of
madd
and maddN
functions have
been updated to be compatible with the OpenCL mad
function and to
generally follow the expanded name, i.e. multiply-add:
madd([], a, b, c)
: before a + b * c
, now: a * b + c
maddN([], a, b, n)
=> maddN([], a, n, b)
(i.e. a * n + b
)
- rename
perpendicularLeft2
=> perpendicularCCW
- rename
perpendicularRight2
=> perpendicularCW
- rename
normalLeft2
/ normalRight2
=> normalCCW
/ normalCW
Related packages
Installation
yarn add @thi.ng/vectors
ESM import:
import * as vec from "@thi.ng/vectors";
Browser ESM import:
<script type="module" src="https://esm.run/@thi.ng/vectors"></script>
JSDelivr documentation
For Node.js REPL:
const vec = await import("@thi.ng/vectors");
Package sizes (brotli'd, pre-treeshake): ESM: 14.01 KB
Dependencies
Note: @thi.ng/api is in most cases a type-only import (not used at runtime)
Usage examples
35 projects in this repo's
/examples
directory are using this package:
Screenshot | Description | Live demo | Source |
---|
| ASCII art raymarching with thi.ng/shader-ast & thi.ng/text-canvas | Demo | Source |
| Basic 2D boid simulation and spatial indexing neighbor lookups | Demo | Source |
| Canvas based dial widget | Demo | Source |
| Self-modifying, animated typographic grid with emergent complex patterns | Demo | Source |
| Interactive visualization of closest points on ellipses | Demo | Source |
| Piechart visualization of CSV data | Demo | Source |
| Doodle w/ K-nearest neighbor search result visualization | Demo | Source |
| K-nearest neighbor search in an hash grid | Demo | Source |
| SVG path to SDF, applying deformation and converting back to SVG | Demo | Source |
| Mouse gesture / stroke analysis, simplification, corner detection | Demo | Source |
| Realtime analog clock demo | Demo | Source |
| Interactive pattern drawing demo using transducers | Demo | Source |
| 2D Bezier curve-guided particle system | Demo | Source |
| Various hdom-canvas shape drawing examples & SVG conversion / export | Demo | Source |
| Canvas based Immediate Mode GUI components | Demo | Source |
| Animated sine plasma effect visualized using contour lines | Demo | Source |
| k-means clustering visualization | Demo | Source |
| Animated, iterative polygon subdivisions & visualization | Demo | Source |
| Quasi-random lattice generator | Demo | Source |
| Unison wavetable synth with waveform editor | Demo | Source |
| Steering behavior drawing with alpha-blended shapes | Demo | Source |
| Minimal rdom-canvas animation | Demo | Source |
| Animated Voronoi diagram, cubic splines & SVG download | Demo | Source |
| 2D scenegraph & shape picking | Demo | Source |
| 2D scenegraph & image map based geometry manipulation | Demo | Source |
| Minimal shader graph developed during livestream #2 | Demo | Source |
| Fitting, transforming & plotting 10k data points per frame using SIMD | Demo | Source |
| Entity Component System w/ 100k 3D particles | Demo | Source |
| Compute cubic spline position & tangent using Dual Numbers | Demo | Source |
| 3D wireframe textmode demo | Demo | Source |
| Visual comparison of biased vs. unbiased normal vectors projected on the surface of a sphere | Demo | Source |
| WebGL multi-colored cube mesh | Demo | Source |
| Drawing to floating point offscreen / multi-pass shader pipeline | Demo | Source |
| WebGL instancing, animated grid | Demo | Source |
| WebGL MSDF text rendering & particle system | Demo | Source |
API
Generated API docs
Basic usage
import * as v from "@thi.ng/vectors";
v.add([], [1, 2, 3, 4], [10, 20, 30, 40]);
a = [1, 2, 3];
v.add(null, a, a);
v.madd([], [10, 20], [0.5, 0.25], [1, 2]);
v.maddN([], [10, 20], 0.5, [1, 2]);
v.addN([], gvec([0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0], 3, 1, 4), 10);
v.addNS3(null, [0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0], 10, 1, 1, 4, 4)
v.dist([1, 2], [100, 200]);
v.distManhattan([1, 2], [100, 200]);
v.distChebyshev([1, 2], [100, 200]);
v.mixN([], [1, 2], [10, 20], 0.5);
v.fromHomogeneous([], [100, 200, 0.5]);
v.swizzle4([], [1, 2], 1, 1, 0, 0);
v.hash([1, 2, 3])
Using the
VecAPI
implementation objects to use size-optimized vector ops in a pluggable manner
(this is in addition/alternative to using the standard set of polymorphic
functions for similar results).
import { VEC2, VEC3, type Vec, type VecAPI } from "@thi.ng/vectors";
interface Particle {
pos: Vec;
dir: Vec;
targetDir: Vec;
speed: number;
turnSpeed: number;
}
const updateParticle = (p: Particle, { maddN, mixN, normalize }: VecAPI) => {
mixN(null, p.dir, p.targetDir, p.turnSpeed);
normalize(null, p.dir);
return maddN(p.pos, p.dir, p.speed, p.pos);
};
let p2d: Particle = {
pos: [10, 20], dir: [0, 1], targetDir: [1, 0], speed: 5, turnSpeed: 0.1,
};
updateParticle(p2d, VEC2);
let p3d: Particle = {
pos: [10, 20, 30], dir: [0, 1, 0], targetDir: [0, 0, 1], speed: 5, turnSpeed: 0.1,
};
updateParticle(p3d, VEC3);
Naming conventions
Wherever possible, each operation comes in different variations. All fixed size
versions use optimized, loop-free implementations.
Suffix | Description |
---|
none | arbitrary length vector arg(s) |
2 | 2d vector arg(s) |
3 | 3d vector arg(s) |
4 | 4d vector arg(s) |
N2 | 2d vector(s) & scalar |
N3 | 3d vector(s) & scalar |
N4 | 4d vector(s) & scalar |
I | arbitrary len, signed int vec |
U | arbitrary len, unsigned int vec |
I2 / I3 / I4 | fixed size signed int vec |
U2 / U3 / U4 | fixed size signed int vec |
NI / NU | arbitrary len, signed int vec & scalar |
NI2 / NI3 / NI4 | fixed size signed int vec & scalar |
NU2 / NU3 / NU4 | fixed size unsigned int vec & scalar |
S2 / S3 / S4 | fixed size strided vec |
NS2 / NS3 / NS4 | fixed size strided vec & scalar |
C | arbitrary len vec, component wise args |
C2 / C3 / C4 | fixed size vec, component wise args |
CS2 / CS3 / CS4 | fixed size strided vec, component args |
Constants
MAX2
/ MAX3
/ MAX4
- each component +Infinity
MIN2
/ MIN3
/ MIN4
- each component -Infinity
ONE2
/ ONE3
/ ONE4
- each component 1
ZERO2
/ ZERO3
/ ZERO4
- each component 0
X2
/ X3
/ X4
- positive X axisY2
/ Y3
/ Y4
- positive Y axisZ3
/ Z4
- positive Z axisW4
- positive W axis
Component setters & copying
set
/ set2
/ set3
/ set4
setC
/ setC2
/ setC3
/ setC4
/ setC6
setN
/ setN2
/ setN3
/ setN4
setS
/ setS2
/ setS3
/ setS4
setCS2
/ setCS3
/ setCS4
setNS2
/ setNS3
/ setNS4
copy
empty
one
zero
Component swizzling
swizzle2
/ swizzle3
/ swizzle4
setSwizzle1
/ setSwizzle2
/ setSwizzle3
/ setSwizzle4
swapXY
/ swapXZ
/ swapYZ
Vector creation
Functions to create wrapped (strided) vector instances:
vec2
/ vec2n
vec3
/ vec3n
vec4
/ vec4n
gvec
Wrap existing vanilla vectors:
Vanilla vector (array) factories:
ones
zeroes
vecOf
setVN3
/ setVN4
setVV4
/ setVV6
/ setVV9
/ setVV16
Basic vector math
Vector / vector
Component wise op with 2 input vectors:
Function | Generic | Fixed | Strided | Int | Comments |
---|
add | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
div | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
mul | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
sub | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
fmod | ✓ | 2-4 | | | (C/JS behavior) |
mod | ✓ | 2-4 | | | (GLSL behavior) |
pow | ✓ | 2-4 | | | |
remainder | ✓ | 2-4 | | | (C behavior) |
import { add, add2, addU2, addS2 } from "@thi.ng/vectors";
add([], [1, 2, 3, 4, 5], [10, 20, 30, 40, 50]);
add2([], [1, 2], [10, 20])
addU2([], [1, -2], [-10, 20])
addS2([], [1,0,2,0], [0,10,0,0,0,20], 0, 0, 1, 1, 2, 4)
Vector / scalar
Component wise op with one input vector and single scalar:
Function | Generic | Fixed | Strided | Int | Comments |
---|
addN | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
divN | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
mulN | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
subN | ✓ | 2-4 | S2-S4 | I2-I4, U2-U4 | |
neg | ✓ | | | | same as mulN(out, v, -1) |
fmodN | ✓ | 2-4 | | | (C/JS behavior) |
modN | ✓ | 2-4 | | | (GLSL behavior) |
powN | ✓ | 2-4 | | | |
remainderN | ✓ | 2-4 | | | (C behavior) |
roundN | ✓ | 2-4 | | | |
Combined operations
Function | Generic | Fixed | Strided | Int | Comments |
---|
addm | ✓ | 2-4 | S2-S4 | | (a + b) * c |
addmN | ✓ | 2-4 | S2-S4 | | (a + b) * n |
madd | ✓ | 2-4 | S2-S4 | | a * n + c |
maddN | ✓ | 2-4 | S2-S4 | | a * n + b |
msub | ✓ | 2-4 | S2-S4 | | a * n - c |
msubN | ✓ | 2-4 | S2-S4 | | a * n - b |
subm | ✓ | 2-4 | S2-S4 | | (a - b) * c |
submN | ✓ | 2-4 | S2-S4 | | (a - b) * n |
Constraints
Function | Generic | Fixed | Strided | Int | Comments |
---|
clamp | ✓ | 2-4 | | | min(max(a, b), c) |
clampN | ✓ | 2-4 | | | min(max(a, n), m) |
clamp01 | ✓ | _2 - _4 | | | min(max(a, 0), 1) |
clamp11 | ✓ | _2 - _4 | | | min(max(a, -1), 1) |
max | ✓ | 2-4 | | | max(a, b) |
min | ✓ | 2-4 | | | min(a, b) |
Cross product
Function | Generic | Fixed | Strided | Int | Comments |
---|
cross | | 2, 3 | S2, S3 | | 2D version returns scalar |
orthoNormal | | 3 | | | |
signedArea | | 2 | | | |
Dot product
Function | Generic | Fixed | Strided | Cwise | Comments |
---|
dot | ✓ | 2-4 | S2-S4 | C4, C6, C8 | |
Interpolation
Function | Generic | Fixed | Strided | Int | Comments |
---|
fit | ✓ | 2-4 | | | |
fit01 | ✓ | _2 - _4 | | | |
fit11 | ✓ | _2 - _4 | | | |
mix | ✓ | 2-4 | S2 - S4 | | |
mixN | ✓ | 2-4 | S2 - S4 | | |
mixBilinear | ✓ | 2-4 | | | |
mixCubic | ✓ | | | | |
mixQuadratic | ✓ | | | | |
smoothStep | ✓ | 2-4 | | | |
step | ✓ | 2-4 | | | |
Normalization / magnitude
Function | Generic | Fixed | Strided | Int | Comments |
---|
limit | ✓ | | | | |
mag | ✓ | | S2-S4 | | |
magSq | ✓ | 2-4 | S2-S4 | | |
normalize | ✓ | | S2-S4 | | w/ opt target length |
Distances
Function | Generic | Fixed | Strided | Int | Comments |
---|
dist | ✓ | | | | |
distSq | ✓ | 2-4 | | | |
distBrayCurtis | ✓ | | | | |
distCanberra | ✓ | | | | |
distChebyshev | ✓ | 2-4 | | | |
distCosine | ✓ | | | | |
distHamming | ✓ | | | | |
distHaversineLatLon | | 2 | | | lat/lon coordinates |
distHaversineLonLat | | 2 | | | lon/lat coordinates |
distJaccard | ✓ | | | | |
distManhattan | ✓ | 2-4 | | | |
distMinkowski | ✓ | | | | |
distSorensenDice | ✓ | | | | |
distWeighted | ✓ | | | | |
pointOnRay | ✓ | 2-3 | | | point at distance |
Orientation
Function | Generic | Fixed | Strided | Int | Comments |
---|
angleBetween | | 2, 3 | | | |
angleRatio | ✓ | | | | |
atan_2 | ✓ | 2-4 | | | Math.atan2(y, x) |
bisect | | 2 | | | |
cornerBisector | ✓ | | | | |
degrees | ✓ | 2-4 | | | |
direction | ✓ | | | | normalize(b - a) |
faceForward | ✓ | | | | |
heading | ✓ | | | | alias headingXY |
headingXY | ✓ | | | | |
headingXZ | ✓ | | | | |
headingYZ | ✓ | | | | |
headingSegment | ✓ | | | | alias headingSegmentXY |
headingSegmentXY | ✓ | | | | |
headingSegmentXZ | ✓ | | | | |
headingSegmentYZ | ✓ | | | | |
normalCCW | | | | | 2D only |
normalCW | | | | | 2D only |
perpendicularCCW | | | | | 2D only |
perpendicularCW | | | | | 2D only |
project | ✓ | | | | |
radians | ✓ | 2-4 | | | |
reflect | ✓ | | | | |
refract | ✓ | | | | |
Rotations
(Also see rotation matrices provided by
@thi.ng/matrices)
Function | Generic | Fixed | Strided | Int | Comments |
---|
rotationAroundAxis | | 3 | | | |
rotationAroundPoint | | 2 | | | |
rotate | | | S2 | | alias for rotateZ |
rotateX | | | S3 | | |
rotateY | | | S3 | | |
rotateZ | | | S3 | | |
Polar / cartesian conversion
Function | Generic | Fixed | Strided | Int | Comments |
---|
cartesian | ✓ | 2, 3 | | | 2D/3D only |
polar | ✓ | 2, 3 | | | 2D/3D only |
Randomness
All ops support custom PRNG impls based on the
@thi.ng/random
IRandom
interface and use Math.random
by default:
Function | Generic | Fixed | Strided | Int | Comments |
---|
jitter | ✓ | | | | |
randMinMax | ✓ | 2-4 | S2-S4 | | |
randNorm | ✓ | 2-4 | S2-S4 | | |
randNormDistrib | ✓ | 2-4 | S2-S4 | | |
random | ✓ | 2-4 | S2-S4 | | |
randomDistrib | ✓ | 2-4 | S2-S4 | | |
Unary vector math ops
Function | Generic | Fixed | Strided | Int | Comments |
---|
abs | ✓ | 2-4 | | | |
acos | ✓ | 2-4 | | | |
asin | ✓ | 2-4 | | | |
atan | ✓ | 2-4 | | | Math.atan(y / x) |
ceil | ✓ | 2-4 | | | |
cos | ✓ | 2-4 | | | |
cosh | ✓ | 2-4 | | | |
exp | ✓ | 2-4 | | | |
floor | ✓ | 2-4 | | | |
fract | ✓ | 2-4 | | | |
fromHomogeneous | ✓ | 3, 4 | | | 3D/4D only |
invert | ✓ | 2-4 | | | |
invSqrt | ✓ | 2-4 | | | |
isInf | ✓ | 2-4 | | | |
isNaN | ✓ | 2-4 | | | |
log | ✓ | 2-4 | | | |
major | ✓ | 2-4 | | | |
minor | ✓ | 2-4 | | | |
round | ✓ | 2-4 | | | |
sign | ✓ | 2-4 | | | |
sin | ✓ | 2-4 | | | |
sinh | ✓ | 2-4 | | | |
sqrt | ✓ | 2-4 | | | |
sum | ✓ | 2-4 | | | |
tan | ✓ | 2-4 | | | |
trunc | ✓ | 2-4 | | | |
wrap | ✓ | 2-4 | | | |
Vector array batch processing
Functions to transform flat / strided buffers w/ vector operations:
mapV
/ mapVN
/ mapVV
/ mapVVN
/ mapVVV
mean
/ median
minBounds
/ maxBounds
Comparison / equality
comparator2
/ comparator3
/ comparator4
equals
/ equals2
/ equals3
/ equals4
eqDelta
/ eqDelta2
/ eqDelta3
/ eqDelta4
eqDeltaS
eqDeltaArray
Bitwise operations (int / uint vec)
Arguments are assumed to be signed / unsigned ints. Results will be
forced accordingly.
Function | Generic | Fixed | Strided | Int | Comments |
---|
bitAnd | ✓ | | | I2-I4, U2-U4 | |
bitAndN | ✓ | | | I2-I4, U2-U4 | |
bitNot | ✓ | | | I2-I4, U2-U4 | |
bitOr | ✓ | | | I2-I4, U2-U4 | |
bitOrN | ✓ | | | I2-I4, U2-U4 | |
bitXor | ✓ | | | I2-I4, U2-U4 | |
bitXorN | ✓ | | | I2-I4, U2-U4 | |
lshift | ✓ | | | I2-I4, U2-U4 | |
rshift | ✓ | | | I2-I4, U2-U4 | |
lshiftN | ✓ | | | I2-I4, U2-U4 | |
rshiftN | ✓ | | | I2-I4, U2-U4 | |
Vector conversions / coercions
asIVec
(2-4) - signed int vectorasUVec
(2-4) - unsigned int vectorasBVec
(2-4) - boolean vectorfromBVec
(2-4) - coerces each component to 0/1
Boolean vector logic
Function | Generic | Fixed | Strided | Int | Comments |
---|
logicAnd | ✓ | 2-4 | | | |
logicAndN | ✓ | 2-4 | | | |
logicOr | ✓ | 2-4 | | | |
logicOrN | ✓ | 2-4 | | | |
logicNot | ✓ | 2-4 | | | |
every | ✓ | 2-4 | | | returns boolean |
some | ✓ | 2-4 | | | returns boolean |
not | ✓ | 2-4 | | | |
Componentwise comparisons
All resulting in boolean vectors:
Function | Generic | Fixed | Strided | Int | Comments |
---|
eq | ✓ | 2-4 | | | |
lt | ✓ | 2-4 | | | |
lte | ✓ | 2-4 | | | |
gt | ✓ | 2-4 | | | |
gte | ✓ | 2-4 | | | |
neq | ✓ | 2-4 | | | |
Hashing
Code generator
compile
/ compileG
/ compileGHOF
/ compileHOF
defOp
/ defOpS
/ defFnOp
/ defHofOp
defMathNOp
/ defMathOp
vop
For more information about the code generator see:
Authors
If this project contributes to an academic publication, please cite it as:
@misc{thing-vectors,
title = "@thi.ng/vectors",
author = "Karsten Schmidt",
note = "https://thi.ng/vectors",
year = 2015
}
License
© 2015 - 2024 Karsten Schmidt // Apache License 2.0